﻿/*=====================================================================
 * Arquivo:	Strings.cs
 * 
 * Sumário: Definição de métodos auxiliares para o tratamento de 
 *          strings.
 * 
 * Autor:		Siddhartha Argollo
 * 
 * Copyright (C) TecnoTRENDS.  Todos os direitos reservados.
 =====================================================================*/
using System;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Diagnostics;

namespace DaCredito
{
    /// <summary>
    /// Esta classe implementa métodos auxiliares para o tratamento de strings
    /// em geral.
    /// </summary>
    public static class StringUtils
    {

        private const string WHITE_SPACES = " \r\n\t";
        private const string LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
        private const string NUMBERS = "0123456789";
        private const string ALPHANUMERIC = LETTERS + NUMBERS;

        /// <summary>
        /// Verifica se a string é nula ou vazia ("").
        /// </summary>
        /// <param name="asValue">Objeto a ser verificado.</param>
        /// <returns>True se a string for nula ou vazia.</returns>
        public static bool IsEmpty(object asValue)
        {
            return (asValue == null) || (asValue.ToString().Trim().Length == 0);
        }

        /// <summary>
        /// Retorna a representação em string do objeto <c>aoValue</c>
        /// ou null se o objeto for null.
        /// </summary>
        /// <param name="aoValue">Objeto a ser utilizado.</param>
        /// <returns>A representação em string do objeto <c>aoValue</c>
        /// ou null se o objeto for null.</returns>
        public static string ToString(object aoValue)
        {
            if (aoValue == null)
                return null;
            return aoValue.ToString();
        }

        /// <summary>
        /// Retorna o valor inteiro <c>aiValue</c> como uma string, formatando-a, se necessário,
        /// com zeros à esquerda, até completar o tamanho especificado em <c>aiSize</c>.
        /// </summary>
        /// <param name="aiValue">Valor inteiro a ser convertido.</param>
        /// <param name="aiSize">Tamanho da string resultante.</param>
        /// <returns>O valor <c>aiValue</c> convertido para string, no tamanho especificado.</returns>
        public static string ToString(int aiValue, int aiSize)
        {
            string lsValue = Convert.ToString(aiValue);
            if (lsValue.Length < aiSize)
                lsValue = StringUtils.Replicate("0", aiSize - lsValue.Length) + lsValue;
            return lsValue;
        }

        /// <summary>
        /// Retorna o valor long <c>alValue</c> como uma string, formatando-a, se necessário,
        /// com zeros à esquerda, até completar o tamanho especificado em <c>aiSize</c>.
        /// </summary>
        /// <param name="alValue">Valor inteiro a ser convertido.</param>
        /// <param name="aiSize">Tamanho da string resultante.</param>
        /// <returns>O valor <c>alValue</c> convertido para string, no tamanho especificado.</returns>
        public static string ToString(long alValue, int aiSize)
        {
            string lsValue = Convert.ToString(alValue);
            if (lsValue.Length < aiSize)
                lsValue = StringUtils.Replicate("0", aiSize - lsValue.Length) + lsValue;
            return lsValue;
        }

        /// <summary>
        /// Precede caracteres especiais com o "~" para a correção da string que for ser
        /// utilizada com alguns métodos do controle DataWindow.
        /// </summary>
        /// <param name="asText">String.</param>
        /// <returns>String convertida.</returns>
        public static string EscapeQuotes(string asText)
        {
            char[] lcInChars = asText.ToCharArray();
            StringBuilder loOutChars = new StringBuilder();
            for (int i = 0; i < lcInChars.Length; i++)
            {
                if (lcInChars[i] == '~')
                    loOutChars.Append("~~");
                else if (lcInChars[i] == '"')
                    loOutChars.Append("~\"");
                else if (lcInChars[i] == '\'')
                    loOutChars.Append("~'");
                else
                    loOutChars.Append(lcInChars[i]);
            }
            return loOutChars.ToString();
        }

        /// <summary>
        /// Retorna a string <c>asText</c> no estilo CamelCase.
        /// </summary>
        /// <remarks>
        /// Este método converte strings no formato CAMEL_CASE para
        /// o formato CamelCase.
        /// </remarks>
        /// <param name="asText">String a ser utilizada.</param>
        /// <returns>A string <c>asText</c> no estilo CamelCase.</returns>
        public static string ToCamelCase(string asText)
        {
            StringBuilder loBuilder = new StringBuilder(asText.Length);
            asText = asText.ToLower();
            bool lbToUpper = true;
            for (int i = 0; i < asText.Length; i++)
            {
                if (asText[i] == '_')
                    lbToUpper = true;
                else
                {
                    if (lbToUpper)
                    {
                        loBuilder.Append(asText[i].ToString().ToUpper());
                        lbToUpper = false;
                    }
                    else
                        loBuilder.Append(asText[i]);
                }
            }
            return loBuilder.ToString();
        }

        /// <summary>
        /// Retorna o valor contido em <c>asValue</c> como inteiro.
        /// Se a conversão não for possível, retorna 0.
        /// </summary>
        /// <param name="asValue">Valor a ser convertido.</param>
        /// <returns>O valor contido em <c>asValue</c> como inteiro.
        /// Se a conversão não for possível, retorna 0.</returns>
        public static int ToInt(string asValue)
        {
            return ToInt(asValue, 0);
        }

        /// <summary>
        /// Retorna o valor contido em <c>asValue</c> como inteiro.
        /// Se a conversão não for possível, retorna o valor
        /// <c>aiDefault</c>.
        /// </summary>
        /// <param name="asValue">Valor a ser convertido.</param>
        /// <param name="aiDefault">Valor a ser retornado em caso
        /// de erro.</param>
        /// <returns>O valor contido em <c>asValue</c> como inteiro.
        /// Se a conversão não for possível, retorna o valor
        /// <c>aiDefault</c>.</returns>
        public static int ToInt(string asValue, int aiDefault)
        {
            int liValue;
            try
            {
                liValue = int.Parse(asValue);
            }
            catch { liValue = aiDefault; }
            return liValue;
        }

        /// <summary>
        /// Retorna o valor contido em <c>asValue</c> como double.
        /// Se a conversão não for possível, retorna 0.
        /// </summary>
        /// <param name="asValue">Valor a ser convertido.</param>
        /// <returns>O valor contido em <c>asValue</c> como double.
        /// Se a conversão não for possível, retorna 0.</returns>
        public static double ToDouble(string asValue)
        {
            return ToDouble(asValue, 0);
        }

        /// <summary>
        /// Retorna o valor contido em <c>asValue</c> como double.
        /// Se a conversão não for possível, retorna o valor
        /// <c>adDefault</c>.
        /// </summary>
        /// <param name="asValue">Valor a ser convertido.</param>
        /// <param name="adDefault">Valor a ser retornado em caso
        /// de erro.</param>
        /// <returns>O valor contido em <c>asValue</c> como double.
        /// Se a conversão não for possível, retorna o valor
        /// <c>adDefault</c>.</returns>
        public static double ToDouble(string asValue, double adDefault)
        {
            double ldValue;
            try
            {
                ldValue = double.Parse(asValue);
            }
            catch { ldValue = adDefault; }
            return ldValue;
        }

        /// <summary>
        /// Retorna a representação em string do objeto <c>aoValue</c>.
        /// Se este for null, retorna uma string vazia.
        /// </summary>
        /// <param name="aoValue">Objeto a ser utilizado.</param>
        /// <returns>A representação em string do objeto <c>aoValue</c>.
        /// Se este for null, retorna uma string vazia.</returns>
        public static string ForceString(object aoValue)
        {
            if (aoValue == null)
                return "";
            return aoValue.ToString();
        }

        /// <summary>
        /// Retorna true se o valor contido em <c>asValue</c> for um
        /// número, false caso contrário.
        /// </summary>
        /// <param name="asValue">Valor a ser verificado.</param>
        /// <returns>True se o valor contido em <c>asValue</c> for um
        /// número, false caso contrário.</returns>
        public static bool IsNumber(string asValue)
        {
            try
            {
                double.Parse(asValue);
                return true;
            }
            catch { /* Nothing to do */ }
            return false;
        }

        /// <summary>
        /// Retorna true se o valor contido em <c>asValue</c> for um
        /// datetime, false caso contrário.
        /// </summary>
        /// <param name="asValue">Valor a ser verificado.</param>
        /// <returns>True se o valor contido em <c>asValue</c> for um
        /// datetime, false caso contrário.</returns>
        public static bool IsDate(string asValue)
        {
            try
            {
                DateTime.Parse(asValue);
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Replica a string <c>asText</c> <c>aiCount</c> vezes.
        /// </summary>
        /// <param name="asText">String a ser replicada.</param>
        /// <param name="aiCount">Quantidade de vezes que a
        /// string será replicada.</param>
        /// <returns>A string <c>asText</c> replicada <c>aiCount</c>
        /// vezes.</returns>
        public static String Replicate(string asText, int aiCount)
        {
            if (aiCount <= 0)
                return "";
            StringBuilder loBuilder = new StringBuilder(aiCount);
            for (int i = 0; i < aiCount; i++)
                loBuilder.Append(asText);
            return loBuilder.ToString();
        }

        /// <summary>
        /// Gets the value portion of a keyword=value pair from a string.
        /// </summary>
        /// <param name="asSource">The string to be searched.</param>
        /// <param name="asKeyWord">The keyword to be searched for.</param>
        /// <param name="asSep">The separator character used in the source 
        /// string.</param>
        /// <returns>The value found for the keyword. If no matching keyword is found, 
        /// an empty string is returned.</returns>
        public static String GetKeyValue(string asSource, string asKeyWord, string asSep)
        {
            // TODO: Testar GetKeyValue
            // Initialize key value
            string lsKeyValue = "";
            bool lbDone = false;
            while (!lbDone)
            {
                int liKeyWord = asSource.ToLower().IndexOf(asKeyWord.ToLower());
                if (liKeyWord >= 0)
                {
                    string lsSource = asSource;
                    asSource = asSource.Substring(liKeyWord + asKeyWord.Length).TrimStart();
                    // See if this is an exact match. Either the match will be at the start of the 
                    // string or the match will be after a separator character. So check for both 
                    // cases.
                    int liEqual = liKeyWord - asSep.Length;
                    if (liEqual > 0)
                    {
                        // not the start so see if this is a compound occurance separated 
                        // by the separator string
                        string lsExact = lsSource.Substring(liEqual, asSep.Length);
                        if (lsExact != asSep)
                            // not the separator string so continue looking
                            continue;
                    }
                    if (asSource[0] == '=')
                    {
                        int liSeparator = asSource.IndexOf(asSep, 1);
                        if (liSeparator > 0)
                            lsKeyValue = asSource.Substring(1, liSeparator - 1);
                        else
                            lsKeyValue = asSource.Substring(1);
                        lsKeyValue = lsKeyValue.Trim();
                        lbDone = true;
                    }
                }
                else
                    lbDone = true;
            }
            return lsKeyValue;
        }

        /// <summary>
        /// Sets the value portion of a keyword=value pair from a string.
        /// </summary>
        /// <param name="asSource">The string to have the set performed on. Passed by 
        /// reference. Format: keyword = value; ...
        /// </param>
        /// <param name="asKeyWord">The keyword to set a value for.</param>
        /// <param name="asKeyValue">The new value for the specified keyword.</param>
        /// <param name="asSep">The separator character used in the source string.</param>
        /// <returns>True if the asKeyWord was found, false otherwise.</returns>
        public static bool SetKeyValue(ref string asSource, string asKeyWord, string asKeyValue, string asSep)
        {
            // Check paramemeters
            /*Debug.Assert(asSource != null, string.Format(Messages.errNotNullParameter, "asSource"));
            Debug.Assert(asKeyWord != null, string.Format(Messages.errNotNullParameter, "asKeyWord"));
            Debug.Assert(asKeyValue != null, string.Format(Messages.errNotNullParameter, "asKeyValue"));
            Debug.Assert(asSep != null, string.Format(Messages.errNotNullParameter, "asSep"));
            */
            bool lbFound = false;
            int liKeyWord = -1;

            do
            {
                liKeyWord = asSource.ToLower().IndexOf(asKeyWord.ToLower(), liKeyWord + 1);
                if (liKeyWord >= 0)
                {
                    string lsTemp = Right(asSource, asSource.Length - (liKeyWord + asKeyWord.Length)).Trim();
                    if (Left(lsTemp, 1) == "=")
                    {
                        int liEqual = asSource.IndexOf("=", liKeyWord + 1);
                        int liSep = asSource.IndexOf(asSep, liEqual + 1);
                        if (liSep >= 0)
                            asSource = Left(asSource, liEqual + 1) + asKeyValue + asSep +
                              Right(asSource, asSource.Length - liSep - 1);
                        else
                            asSource = Left(asSource, liEqual + 1) + asKeyValue;
                        lbFound = true;
                    }
                }
            }
            while (liKeyWord >= 0);

            return lbFound;
        }

        public static string Left(string asSource, int size)
        {
            if (asSource == null)
                return null;
            if (asSource.Length >= size)
                return asSource.Substring(0, size);
            return asSource;
        }

        public static string Right(string asSource, int size)
        {
            if (asSource == null)
                return null;
            if (asSource.Length >= size)
                return asSource.Substring(asSource.Length - size, size);
            return "";
        }

        /// <summary>
        /// Parse a string into array elements using a delimeter string.
        /// </summary>
        /// <param name="asArray">The array to be filled with the parsed strings.</param>
        /// <param name="asSource">The string to parse.</param>
        /// <param name="asDelimiter">The delimeter string.</param>
        public static void ParseToArray(ref string[] asArray, string asSource, string asDelimiter)
        {
            ArrayList loArray = new ArrayList();
            ParseToArray(loArray, asSource, asDelimiter);
            asArray = (string[])loArray.ToArray(typeof(string));
        }

        /// <summary>
        /// Parse a string into array elements using a delimeter string.
        /// </summary>
        /// <param name="aoArray">The array to be filled with the parsed strings.</param>
        /// <param name="asSource">The string to parse.</param>
        /// <param name="asDelimiter">The delimeter string.</param>
        public static void ParseToArray(ArrayList aoArray, string asSource, string asDelimiter)
        {
            //Check for NULL
            if (!(IsEmpty(asSource) || IsEmpty(asSource)))
            {
                //Get the length of the delimeter
                int liDelLen = asDelimiter.Length;
                int liPos = asSource.ToUpper().IndexOf(asDelimiter.ToUpper());
                //Only one entry was found
                if (liPos == -1)
                    aoArray.Add(asSource);
                else
                {
                    //More than one entry was found - loop to get all of them
                    string lsHolder;
                    int liLength, liStart = 0;
                    while (liPos >= 0)
                    {
                        //Set current entry
                        liLength = liPos - liStart;
                        lsHolder = asSource.Substring(liStart, liLength);
                        // Update array
                        aoArray.Add(lsHolder);
                        //Set the new starting position
                        liStart = liPos + liDelLen;
                        liPos = asSource.ToUpper().IndexOf(asDelimiter.ToUpper(), liStart);
                    }
                    //Set last entry
                    lsHolder = asSource.Substring(liStart);
                    // Update array if necessary
                    if (lsHolder.Length > 0)
                        aoArray.Add(lsHolder);
                }
            }
        }

        /// <summary>
        /// This function strips a source string (from the left) up to the 
        /// occurrence of a specified separator character.
        /// </summary>
        /// <param name="asSource"></param>
        /// <param name="asSep"></param>
        /// <returns>The token stripped off of the source string. If the separator 
        /// character does not appear in the string, the entire source string 
        /// is returned. Otherwise, it returns the token stripped off of the left 
        /// end of the source string (not including the separator character).
        /// If any argument's value is NULL, function returns NULL.</returns>
        public static string GetToken(ref string asSource, string asSep)
        {
            string lsResult = null;
            if (asSource != null && asSep != null)
            {
                // Get the position of the separator
                int liPos = asSource.IndexOf(asSep);
                // Compute the length of the token to be stripped off of the source string.
                // If no separator, the token to be stripped is the entire source string.
                if (liPos == 0)
                {
                    lsResult = asSource;
                    asSource = "";
                }
                else
                {
                    // Otherwise, return just the token and strip it and the separator
                    // from the source string
                    lsResult = asSource.Substring(0, liPos);
                    asSource = asSource.Substring(liPos + 1);
                }
            }
            return lsResult;
        }

        /// <summary>
        /// Codifica o objeto <c>aoColor</c> para uma string no formato RGB em hexadecimal.
        /// </summary>
        /// <remarks>
        /// Este método elimina a parte alfa retornada pelo método ToArgb do objeto Color, 
        /// estando o valor resultante pronto para ser usado em parâmetros HTML.
        /// </remarks>
        /// <param name="aoColor">Objeto Color.</param>
        /// <returns>A cor recebida em uma string no formato RGB.</returns>
        public static string ColorToRGB(Color aoColor)
        {
            string lsColor = string.Format("{0:X}", aoColor.ToArgb());
            // Retira a parte Alfa (AARRGGBB)
            if (lsColor.Length > 6)
                return lsColor.Substring(2);
            return lsColor;
        }

        /// <summary>
        /// Faz uma comparação LIKE entre o texto <c>asText</c> e o padrão
        /// <c>asPattern</c>, ignorando maiúsculas e minúsculas e usando os
        /// símbolos "%" e "_" do LIKE do SQLServer.
        /// </summary>
        /// <param name="asText">Texto a ser comparado com o padrão.</param>
        /// <param name="asPattern">Padrão que o texto deve seguir.</param>
        /// <returns>
        /// True se o texto recebido obedecer ao padrão, false caso contrário.
        /// </returns>
        public static bool Like(string asText, string asPattern)
        {
            return Like(asText, asPattern, "%_", true);
        }

        /// <summary>
        /// Faz uma comparação LIKE entre o texto <c>asText</c> e o padrão
        /// <c>asPattern</c>, ignorando maiúsculas e minúsculas.
        /// </summary>
        /// <param name="asText">Texto a ser comparado com o padrão.</param>
        /// <param name="asPattern">Padrão que o texto deve seguir.</param>
        /// <param name="asLikeSymbol">
        /// Símbolos usados para a pesquisa do LIKE. O primeiro deve ser o símbolo
        /// de substituição de vários caracteres e o segundo deve ser o símbolo
        /// de substituição de apenas um caractere. Por exemplo: "%_".
        /// </param>
        /// <returns>
        /// True se o texto recebido obedecer ao padrão, false caso contrário.
        /// </returns>
        public static bool Like(string asText, string asPattern, string asLikeSymbol)
        {
            return Like(asText, asPattern, asLikeSymbol, true);
        }

        /// <summary>
        /// Faz uma comparação LIKE entre o texto <c>asText</c> e o padrão
        /// <c>asPattern</c>.
        /// </summary>
        /// <param name="asText">Texto a ser comparado com o padrão.</param>
        /// <param name="asPattern">Padrão que o texto deve seguir.</param>
        /// <param name="asLikeSymbols">
        /// Símbolos usados para a pesquisa do LIKE. O primeiro deve ser o símbolo
        /// de substituição de vários caracteres e o segundo deve ser o símbolo
        /// de substituição de apenas um caractere. Por exemplo: "%_".
        /// </param>
        /// <param name="abIgnoreCase">
        /// Indica se deve ignorar maiúsculas e minúsculas.
        /// </param>
        /// <returns>
        /// True se o texto recebido obedecer ao padrão, false caso contrário.
        /// </returns>
        public static bool Like(string asText, string asPattern, string asLikeSymbols,
          bool abIgnoreCase)
        {
            StringBuilder loPattern = new StringBuilder(Regex.Escape(asPattern));
            loPattern.Replace(Regex.Escape(asLikeSymbols[0].ToString()), ".*");
            loPattern.Replace(Regex.Escape(asLikeSymbols[1].ToString()), ".");
            loPattern.Replace(Regex.Escape("["), "[");
            loPattern.Replace(Regex.Escape("]"), "]");
            loPattern.Insert(0, "^");
            loPattern.Append("$");
            if (abIgnoreCase)
                return Regex.IsMatch(asText, loPattern.ToString(), RegexOptions.IgnoreCase);
            return Regex.IsMatch(asText, loPattern.ToString());
        }

        /// <summary>
        /// Este método quebra uma string formada por pares chave=valor
        /// separados por espaços, retornando um array contendo
        /// o nomes das chaves e outro contendo seus respectivos valores,
        /// respeitando os valores que estiverem entre aspas.
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="asKeys">Chaves encontradas no texto.</param>
        /// <param name="asValues">Valores encontrados no texto.</param>
        /// <returns>A quantidade de valores encontrados.</returns>
        public static int SplitKeyList(string asText, out string[] asKeys, out string[] asValues)
        {
            return SplitKeyList(asText, out asKeys, out asValues, WHITE_SPACES);
        }

        /// <summary>
        /// Este método quebra uma string formada por pares chave=valor
        /// separados pelos caracteres em <c>asSepChars</c>, retornando um array 
        /// contendo o nomes das chaves e outro contendo seus respectivos valores,
        /// respeitando os valores que estiverem entre aspas.
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="asKeys">Chaves encontradas no texto.</param>
        /// <param name="asValues">Valores encontrados no texto.</param>
        /// <param name="asSepChars">Separadores dos valores.</param>
        /// <returns>A quantidade de valores encontrados.</returns>
        public static int SplitKeyList(string asText, out string[] asKeys, out string[] asValues,
          string asSepChars)
        {
            int liEnd;
            int liPos = SkipUntil(asText, "=");
            List<string> lsKeys = new List<string>();
            List<string> lsValues = new List<string>();

            while (asText.Trim() != "")
            {
                // Recupera a chave
                string lsKey = asText.Substring(0, liPos).Trim();
                // Verifica o primeiro char após o igual
                liPos = Skip(asText, asSepChars, liPos + 1, 1);
                char ch = asText[liPos];
                // Se aspas, busca até o fim
                if (ch == '"' || ch == '\'')
                    liEnd = SkipUntil(asText, ch.ToString(), liPos + 1, 1) + 1;
                else
                    liEnd = SkipUntil(asText, asSepChars, liPos + 1, 1);
                // Recupera o valor
                string lsValue = asText.Substring(liPos, liEnd - liPos);
                if (lsValue.Trim() != "")
                {
                    lsKeys.Add(lsKey);
                    lsValues.Add(lsValue);
                }
                liEnd = Skip(asText, asSepChars, liEnd, 1);
                asText = asText.Substring(liEnd);
                liPos = SkipUntil(asText, "=", 1, 1);
            }

            asKeys = lsKeys.ToArray();
            asValues = lsValues.ToArray();

            return lsValues.Count;
        }

        /// <summary>
        /// Este método adiciona 1 a partir da posição inicial 0,
        /// enquanto os caracteres varridos estejam dentro
        /// da string asChars.
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="asChars">Caracteres que devem ser pulados.</param>
        /// <returns></returns>
        public static int Skip(string asText, string asChars)
        {
            return Skip(asText, asChars, 0, 1);
        }

        /// <summary>
        /// Este método adiciona aiStep a partir da posição inicial
        /// aiStart, enquanto os caracteres varridos estejam dentro
        /// da string asChars. O parâmetro aiStep deve ser 1 para
        /// avanço ou -1 para retrocesso.
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="asChars">Caracteres que devem ser pulados.</param>
        /// <param name="aiStart">Posição inicial da análise.</param>
        /// <param name="aiStep">Passo da análise (-1 ou 1)</param>
        /// <returns></returns>
        public static int Skip(string asText, string asChars, int aiStart, int aiStep)
        {
            int liLen = asText.Length;
            int liPos = aiStart;

            while (liPos >= 0 && liPos < liLen && asChars.IndexOf(asText[liPos]) >= 0)
                liPos += aiStep;

            return liPos;
        }

        /// <summary>
        /// Este método adiciona 1 a partir da posição inicial 0, até encontrar 
        /// um dos caracteres definiodos em asEndChars.
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="asEndChars">Caracteres que deve ser encontrados.</param>
        /// <returns>A posição onde está o caractere asEndChars.</returns>
        public static int SkipUntil(string asText, string asEndChars)
        {
            return SkipUntil(asText, asEndChars, 0, 1);
        }

        /// <summary>
        /// Este método adiciona aiStep a partir da posição inicial
        /// aiStart, até encontrar um dos caracteres definiodos em asEndChars.
        /// O parâmetro aiStep deve ser 1 para avanço ou -1 para retrocesso.
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="asEndChars">Caracteres que deve ser encontrados.</param>
        /// <param name="aiStart">Posição inicial da análise.</param>
        /// <param name="aiStep">Passo da análise (-1 ou 1).</param>
        /// <returns>A posição onde está o caractere asEndChars.</returns>
        public static int SkipUntil(string asText, string asEndChars, int aiStart, int aiStep)
        {
            int liLen = asText.Length;
            int liPos = aiStart;

            while (liPos >= 0 && liPos < liLen && asEndChars.IndexOf(asText[liPos]) < 0)
                liPos += aiStep;

            return liPos;
        }

        /// <summary>
        /// <para>Este método adiciona aiStep a partir da posição inicial aiStart, até encontrar 
        /// o caractere acCloseChar que termina acOpenChar. Considera que a pesquisa se 
        /// inicia APÓS o caractere as_openchar inicial ou A PARTIR do mesmo.</para>
        /// <para>O parâmetro aiStep deve ser 1 para avanço ou -1 para retrocesso.</para>
        /// <para>Ex.: Para encontrar o ')' final na string: "(x * ((y + 4) / 2))" aiStart 
        /// deve ser igual a 1.</para>
        /// </summary>
        /// <param name="asText">Texto a ser analisado.</param>
        /// <param name="acOpenChar">Caractere inicial.</param>
        /// <param name="acCloseChar">Caractere final.</param>
        /// <param name="aiStart">Posição inicial da análise.</param>
        /// <param name="aiStep">Passo da análise (-1 ou 1)</param>
        /// <returns></returns>
        public static int SkipToEnd(string asText, char acOpenChar, char acCloseChar, int aiStart, int aiStep)
        {
            int liCount = 1;
            int liPos = aiStart;
            int liLen = asText.Length;
            char ch = asText[liPos];

            // Se estiver em cima de acOpenChar, avança uma posição
            if (ch == acOpenChar)
                liPos += 1;

            // Procura o caractere acCloseChar correspondente
            while (liPos <= liLen)
            {
                ch = asText[liPos];
                if (ch == acOpenChar)
                    liCount += 1;
                if (ch == acCloseChar)
                    liCount -= 1;
                if (liCount == 0)
                    break;
                liPos += aiStep;
            }

            return liPos;
        }

        /// <summary>
        /// Retira as apas de uma string.
        /// </summary>
        /// <param name="asText">String a ser analisada.</param>
        /// <returns>A string contida em asText sem aspas.</returns>
        public static string Unquote(string asText)
        {
            string lsCh;
            lsCh = asText.Substring(0, 1);
            if (lsCh == "\"" || lsCh == "'")
                asText = asText.Substring(1, asText.Length - 2);
            return asText;
        }

        public static int CountOccurrences(string asSource, string asTarget)
        {
            return CountOccurrences(asSource, asTarget, true);
        }

        public static int CountOccurrences(string asSource, string asTarget, bool abIgnoreCase)
        {
            // Check for parameters
           // Debug.Assert(asSource != null, string.Format(Messages.errNotNullParameter, "asSource"));
           // Debug.Assert(asTarget != null, string.Format(Messages.errNotNullParameter, "asTarget"));

            // Should function ignore case?
            if (abIgnoreCase)
            {
                asSource = asSource.ToLower();
                asTarget = asTarget.ToLower();
            }

            int liLen = asTarget.Length;
            int liCount = 0;

            int liPos = asSource.IndexOf(asTarget);

            while (liPos >= 0)
            {
                liCount++;
                liPos = asSource.IndexOf(asTarget, liPos + liLen);
            }

            return liCount;
        }

        /// <summary>
        /// Retrieve a token by element index.
        /// </summary>
        /// <param name="asSource">String to be searched.</param>
        /// <param name="asSeparator">Separator of tokens.</param>
        /// <param name="aiIndex">Index of item.</param>
        /// <returns></returns>
        public static string GetTokenByIndex(string asSource, string asSeparator, int aiIndex)
        {
            asSource = asSource + asSeparator;
            for (int i = 0; i < aiIndex; i++)
            {
                asSource = asSource.Substring(asSource.IndexOf(asSeparator) + 1);
                if (string.IsNullOrEmpty(asSource))
                    break;
            }
            return Left(asSource, asSource.IndexOf(asSeparator));
        }

        public static string ToHex(string asColor, int aiSize)
        {
            return ToHex(Convert.ToInt32(asColor), aiSize);
        }

        public static string ToHex(int aiColor, int aiSize)
        {
            string lsValue = Convert.ToString(aiColor, 16);
            if (lsValue.Length < aiSize)
                lsValue = StringUtils.Replicate("0", aiSize - lsValue.Length) + lsValue;
            return lsValue;
        }

        public static IDictionary<string, string> GetKeyMap(string asSource, string asSep)
        {
            if (string.IsNullOrEmpty(asSource))
                return null;

            string[] lsKeys;
            string[] lsValues;
            Dictionary<string, string> loResult = new Dictionary<string, string>();

            SplitKeyList(asSource, out lsKeys, out lsValues, asSep);

            for (int i = 0; i < lsKeys.Length; i++)
                loResult.Add(lsKeys[i], lsValues[i]);

            return loResult;
        }

        /// <summary>
        /// Fills the fields of a template string using the values specified in the variables dictionary.
        /// It searches for strings like {{ variable_name }} and replaces them with aoVariables["variable_name"].
        /// </summary>
        /// <param name="asTemplate">template string with fields enclosed by two braces</param>
        /// <param name="aoVariables">dictionary that maps the variables in the template to values</param>
        /// <returns>a string with the variable fields replaced by their mapped values, or empty string if no value was
        /// given for the variable name</returns>
        public static string FillTemplate(string asTemplate, Dictionary<string, string> aoVariables)
        {
            const string PATTERN = "{{{{\\ *({0})\\ *}}}}";
            string lsReturn = asTemplate;
            foreach (KeyValuePair<string, string> loVariable in aoVariables)
                lsReturn = Regex.Replace(lsReturn, String.Format(PATTERN, Regex.Escape(loVariable.Key)), loVariable.Value);
            lsReturn = Regex.Replace(lsReturn, String.Format(PATTERN, ".*?"), String.Empty);
            return lsReturn;
        }

    } // StringUtils

} // Trends.TFC.Util
